GUACAMOLE-2281: Add auth-challenge and auth-response instructions to libguac.#675
GUACAMOLE-2281: Add auth-challenge and auth-response instructions to libguac.#675aleitner wants to merge 1 commit into
Conversation
e5c7912 to
4eca0a8
Compare
|
Maybe add a UT for the new framework? Similar to |
|
Thinking through shared sessions: since send_req() broadcasts to all connected users, we would receive multiple "done" responses for a single request. Could we follow the existing This follows the existing RDP & VNC pattern of prompting only the session owner for authentication-related input. |
|
I think this (and it's companion: apache/guacamole-client#1218) are actually drafts & not intended to be merged at this point. Maybe we should change the PR status? |
Okay, I've marked them as Draft for now. |
8258dcc to
1688641
Compare
|
Added unit tests in the latest push |
bbennett-ks
left a comment
There was a problem hiding this comment.
I looked over the existing primitives (msg, pipe, argv) to understand the need for a new RPC set, and this is a good extension: none of them offer a correlated request/response, whereas this provides:
- Bidirectional request/response channel (either side can initiate).
- Client-generated UUID correlation that stays unique across the multiple processes in the round-trip workflow (where a per-connection stream/object index couldn't).
- Payload breakup & reassembly: the body streams over a paired pipe as blobs and is reassembled by libguac, allowing payloads larger than a single instruction can carry.
- Cancellation support.
5e30d6f to
6923c20
Compare
mike-jumper
left a comment
There was a problem hiding this comment.
I'm mainly not sure about the set of req/done/cancel instructions and use of pipe. The structure resembles the established "object" pattern that we already use for filesystem, so in this case I'd lean more toward an object-style service that can be given a name.
For example:
servicedeclares a service object namedwebauthn.- Client retrieves the object's JSON index of available streams, which here would be
createandget. - When the client wishes to issue a request, it creates a
putstream to the relevant named stream of the object, including an opaque request ID in the path (ie:create/SOME_ID). - When the server needs to send the response for a request, it issues a
bodyfor the same path.
Would that align with what you're seeing with the structure of WebAuthN (and possibly plans for USB)?
A couple clarifications before reshaping the PR. WebAuthn passthrough is server initiated. The server forwards a challenge from upstream and the browser is the responder. The existing object pattern has get and put flowing client to server with body as the response back, and I couldn't find an example of a server-pushed body without a preceding client get. Would the shape you have in mind involve a server-issued put toward the client carrying the request, or is there an existing primitive for server-initiated push I'm missing |
|
A challenge/response flow could just be:
I add the |
6923c20 to
9be1af5
Compare
Okay so I am working on a redesign. Two new stream-opening instructions, The mimetype identifies the ceremony kind. I went with |
| * } | ||
| * @endcode | ||
| */ | ||
| void (*destructor)(void* data); |
There was a problem hiding this comment.
The established pattern here is a free_handler, which would need its own stream-specific typedef (just like the other structs).
There was a problem hiding this comment.
Renamed to free_handler and added a guac_stream_free_handler typedef in user-fntypes.h alongside the other stream handler typedefs
| /* Run the registered destructor for the stream's data, if any */ | ||
| if (stream->destructor != NULL) { | ||
| stream->destructor(stream->data); | ||
| stream->destructor = NULL; | ||
| stream->data = NULL; | ||
| } |
There was a problem hiding this comment.
Good idea to NULL out stream->data here, but that should probably be done more broadly and regardless of whether a destructor is provided. For example:
if (stream->destructor != NULL)
stream->destructor(stream->data);
/* Clean up any remaining dangling pointers before permitting reuse of the
* stream */
stream->data = NULL;
stream->destructor = NULL;
stream->another_handler = NULL;
stream->yet_another_handler = NULL;There was a problem hiding this comment.
Moved the cleanup out of the if-block. data, ack_handler, blob_handler, end_handler, and free_handler are now all NULL'd unconditionally before the slot is released. Same fix in guac_client_free_stream
| * Sender for the auth-response test: emits send_auth_response referencing | ||
| * an earlier challenge id. | ||
| */ | ||
| static void write_auth_response(int fd) { |
There was a problem hiding this comment.
Same here - we need to document all parameters.
There was a problem hiding this comment.
Removed along with send_auth_response, nothing emits auth-response from the server. See the map entry for the removed auth-challenge
| {"usbconnect", __guac_handle_usbconnect}, | ||
| {"usbdata", __guac_handle_usbdata}, | ||
| {"usbdisconnect", __guac_handle_usbdisconnect}, | ||
| {"auth-challenge", __guac_handle_auth_challenge}, |
There was a problem hiding this comment.
What are the semantics of receiving an auth challenge from the user?
There was a problem hiding this comment.
Removed auth-challenge since nothing registers a handler. Also dropped send_auth_response, the typedef, and the field since the server only emits auth-challenge and only receives auth-response. Same on the JS side, dropped the auth-response handler, onauthresponse, and sendAuthChallenge. Can add the reverse direction back if anything else needs it
3a1ec95 to
64d2079
Compare
| * @param data | ||
| * The stream's data pointer at the time the stream is being freed. | ||
| */ | ||
| typedef void guac_stream_free_handler(void* data); |
There was a problem hiding this comment.
This should receive the guac_stream being freed.
There was a problem hiding this comment.
Switched the typedef to take the stream and updated the call sites and the test handler
| /** Number of times the capturing handler has been invoked. */ | ||
| int call_count; | ||
|
|
||
| /** Stream index of the most recent invocation's stream. */ | ||
| int stream_index; | ||
|
|
||
| /** Mimetype argument of the most recent invocation. */ | ||
| char mimetype[64]; | ||
|
|
||
| /** Challenge id argument of the most recent invocation. */ | ||
| char challenge_id[64]; |
There was a problem hiding this comment.
These should match the:
/**
* Doxygen block comments used elsewhere.
*/rather than:
/** Single-line Doxygen comment (which has no precedent here). */There was a problem hiding this comment.
Converted to block style
| /* Pull corresponding stream */ | ||
| int stream_index = atoi(argv[0]); | ||
| guac_stream* stream = __init_input_stream(user, stream_index); | ||
| if (stream == NULL) | ||
| return 0; | ||
|
|
||
| /* If supported, call handler */ | ||
| if (user->auth_response_handler) | ||
| return user->auth_response_handler( | ||
| user, | ||
| stream, | ||
| argv[1], /* mimetype */ | ||
| argv[2] /* challenge_id */ | ||
| ); |
There was a problem hiding this comment.
argc needs to be verified before dereferencing argv.
There was a problem hiding this comment.
Added an argc guard ahead of the argv reads
…libguac. Adds two new protocol instructions for authentication exchanges: - auth-challenge: opens a stream carrying the body of a challenge for a pending authentication exchange. The wire form is "auth-challenge,<stream_idx>,<mimetype>,<challenge_id>". The mimetype identifies the auth flavor; the challenge_id is an opaque identifier the peer references in its matching auth-response. - auth-response: opens a stream carrying the response body for a previously-issued auth-challenge identified by the same challenge_id. Same wire shape as auth-challenge. Each instruction's body rides as blobs on the announced stream, terminated by end. No paired pipe, no single-slot per-user buffering; per-stream blob and end handlers do the work. Also adds a destructor callback to guac_stream, called from guac_user_free_stream / guac_client_free_stream and from guac_user_free / guac_client_free for streams still open at disconnect, so consumers can attach per-stream state that needs cleanup if the peer drops mid-stream. The first consumer is WebAuthn passthrough (companion PR on guacamole-client) using application/x-webauthn-create+json and application/x-webauthn-get+json mimetypes.
64d2079 to
5a7a7fd
Compare
Adds two new protocol instructions for authentication exchanges:
auth-challenge: opens a stream carrying the body of a challenge for a pending authentication exchange. Wire form is auth-challenge,<stream_idx>,,<challenge_id>. The mimetype identifies the auth flavor; the challenge_id is an opaque identifier the peer references in its matching auth-response.
auth-response: opens a stream carrying the response body for a previously-issued auth-challenge identified by the same challenge_id. Same wire shape as auth-challenge.
Each instruction's body rides as blobs on the announced stream, terminated by end.
Also adds a destructor (free_handler) on guac_stream, called from guac_user_free_stream / guac_client_free_stream and from guac_user_free / guac_client_free for streams still open at disconnect, so consumers can attach per-stream state that needs cleanup if the peer drops mid-stream.
The first consumer is WebAuthn passthrough (companion PR apache/guacamole-client#1218) using application/x-webauthn-create+json and application/x-webauthn-get+json mimetypes.
Guacamole client PR